//
// Created by zhouyi on 2019-11-29.
//

#include "VideoChannel.h"

extern "C" {
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/rational.h>
}

VideoChannel::VideoChannel(int channelId, JavaCallHelper *helper, AVCodecContext *avCodecContext,
                           const AVRational &base, int fps) : BaseChannel(channelId, helper, avCodecContext, base),
                                                              fps(fps) {


    pthread_mutex_init(&surfaceMutex, 0);

}

VideoChannel::~VideoChannel() {
    pthread_mutex_destroy(&surfaceMutex);
}


void *asyncVideoDecode_t(void *args) {
    VideoChannel *videoChannel = static_cast<VideoChannel *>(args);
    videoChannel->decode();
    return nullptr;
}

void *asyncVideoPlay_t(void *args) {
    VideoChannel *videoChannel = static_cast<VideoChannel *>(args);
    videoChannel->_play();
    return nullptr;
};


void VideoChannel::_play() {
    // 缩放与格式转换
    SwsContext *swsContext = sws_getContext(avCodecContext->width, avCodecContext->height, avCodecContext->pix_fmt,
                                            avCodecContext->width,
                                            avCodecContext->height, AV_PIX_FMT_RGBA, SWS_FAST_BILINEAR, nullptr,
                                            nullptr, nullptr);
    /**
     * 第一步，初始化变量
     */
    //转换后的数据与每行数据字节数
    uint8_t *dst_data[4];
    int dst_linesSize[4];
    // ffmpeg 根据格式申请堆内存
    av_image_alloc(dst_data, dst_linesSize, avCodecContext->width, avCodecContext->height, AV_PIX_FMT_RGBA, 1);

    AVFrame *avFrame = nullptr;
    int ret;
    while (isPlaying) {
        /**
         * 第二步获取frame方法
         */
        // 阻塞方法
        ret = frame_queue.deQueue(avFrame);
        // 可能用户已经定制播放了
        // 由于FFmpeg在解码视频时一般情况而言视频数据会被解码为YUV数据，而ANativeWindow并不能直接显示YUV数 据的图像，
        // 所以需要将YUV转换为RGB进行显示。而FFmpeg的swscale模块就提供了颜色空间转换的功能
        // FFmpeg的swscale转换效率可能存在问题，如ijkPlayer中使用的是google的libyuv库进行的转换
        if (!isPlaying) {
            break;
        }


        if (!ret) {
            continue;
        }

        /**
         * 第三步frame 数据进行转换
         */
        // 参数二 指针数据，比如RGBA，每个一个维度的数据就是一个指针，那么RGBA需要4个指针，所以就是4个元素的数组，数组的元素就是指针，指针数据
        // 参数三 每一行数据他的数据个数
        // 参数四 offset
        // 参数五 要转化图像的高
        sws_scale(swsContext, avFrame->data, avFrame->linesize, 0,
                  avFrame->height, dst_data,
                  dst_linesSize);

        /**
         * 第四步 转换好的数据绘制
         */
        onDraw(dst_data, dst_linesSize, avCodecContext->width, avCodecContext->height);
    }

    av_free(&dst_data);
    isPlaying = false;
    releaseAvFrame(avFrame);
    sws_freeContext(swsContext);
}


void VideoChannel::onDraw(uint8_t **dst_data, int *dst_linesSize, int width, int height) {
    pthread_mutex_lock(&surfaceMutex);
    if (!window) {
        pthread_mutex_unlock(&surfaceMutex);
        return;
    }

    ANativeWindow_setBuffersGeometry(window, width, height, WINDOW_FORMAT_RGBA_8888);

    ANativeWindow_Buffer buffer;

    // 把视频数据刷到buffer上，涉及到字节对齐  64字节对齐
    // 绑定是可能失败的，失败原因就是window不可用了
    // ANativeWindow_lock 0 for success
    if (ANativeWindow_lock(window, &buffer, nullptr) != 0) {
        // lock 失败
        ANativeWindow_release(window);
        window = nullptr;
        pthread_mutex_unlock(&surfaceMutex);
        return;
    }

    uint8_t *dstData = static_cast<uint8_t *>(buffer.bits);
    // buffer.stride步长
    int dstSize = buffer.stride * 4;

    uint8_t *srcData = dst_data[0];
    int srcSize = dst_linesSize[0];

    // 一行一行的拷贝
    for (int i = 0; i < buffer.height; ++i) {
        memcpy(dstData + i * dstSize, srcData + i * srcSize, srcSize);
    }

    ANativeWindow_unlockAndPost(window);
    pthread_mutex_unlock(&surfaceMutex);

}


void VideoChannel::play() {

    isPlaying = true;
    setEnable(true);
    // 解码
    pthread_create(&videoDecodeTask, nullptr, asyncVideoDecode_t, this);
    // 播放
    pthread_create(&videoPlayTask, nullptr, asyncVideoPlay_t, this);

}

void VideoChannel::stop() {
    isPlaying = false;

}

void VideoChannel::decode() {
    AVPacket *avPacket = nullptr;

    while (isPlaying) {
        // 阻塞
        // 1. 能够取到数据
        // 2. 停止播放
        int ret = pkt_queue.deQueue(avPacket);
        if (!isPlaying) {
            break;
        }

        if (!ret) {
            continue;
        }

        ret = avcodec_send_packet(avCodecContext, avPacket);
        releaseAvPacket(avPacket);
        if (ret < 0) {
            break;
        }

        AVFrame *avFrame = av_frame_alloc();
        ret = avcodec_receive_frame(avCodecContext, avFrame);
        if (ret == AVERROR(EAGAIN)) {
            // 需要更多帧 I帧(关键帧) B帧(参考帧，需要后续帧的内容) P帧(参考帧)
            continue;
        } else if (ret < 0) {
            break;
        }
        frame_queue.enQueue(avFrame);
    }
    releaseAvPacket(avPacket);

}

void VideoChannel::setWindow(ANativeWindow *pWindow) {
    pthread_mutex_lock(&surfaceMutex);
    if (this->window) {
        ANativeWindow_release(this->window);
    }
    this->window = pWindow;
    pthread_mutex_unlock(&surfaceMutex);

}






